/*
 * Copyright 1993, 2000 Christopher Seiwald.
 *
 * This file is part of Jam - see jam.c for Copyright information.
 */

/*  This file is ALSO:
 *  Copyright 2001-2004 David Abrahams.
 *  Distributed under the Boost Software License, Version 1.0.
 *  (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef COMPILE_DWA20011022_H
# define COMPILE_DWA20011022_H

#ifdef _MSC_VER
#pragma once
#endif

#include "jam.hpp"

#include <boost/intrusive_ptr.hpp>

//Set operation for set_node, etc
enum assignment_operation{
	ASSIGN_SET,     //= assign variable
	ASSIGN_APPEND,  //+= append variable
	ASSIGN_DEFAULT, //set only if unset
};
inline assignment_operation to_assignment(int val)
{
	assert(val>=ASSIGN_SET && val<=ASSIGN_DEFAULT);
	return assignment_operation(val);
}

/* Flags for compile_setexec() */
#define EXEC_UPDATED	0x01	/* executes updated */
#define EXEC_TOGETHER	0x02	/* executes together */
#define EXEC_IGNORE		0x04	/* executes ignore */
#define EXEC_QUIETLY	0x08	/* executes quietly */
#define EXEC_PIECEMEAL	0x10	/* executes piecemeal */
#define EXEC_EXISTING	0x20	/* executes existing */

//Conditions for eval_node and if_node
enum condition_type{
	EXPR_NOT, // ! cond
	EXPR_AND, // cond && cond
	EXPR_OR,  // cond || cond

	EXPR_EXISTS, // arg
	EXPR_EQUALS, // arg = arg
	EXPR_NOTEQ,	 // arg != arg
	EXPR_LESS,	 // arg < arg
	EXPR_LESSEQ, // arg <= arg
	EXPR_MORE,	 // arg > arg
	EXPR_MOREEQ, // arg >= arg
	EXPR_IN,	 // arg in arg
};

///////////////////////////////////////////////////////////////////////////////////////
////// Parse tree node
///////////////////////////////////////////////////////////////////////////////////////
struct parse_node_base;
struct parse_node_visitor;

struct JAMLIB_EXPORT parse_node_base : public ref_count_holder
{
	jam_string file_;
	int line_;

	parse_node_base() : line_() {}
	virtual ~parse_node_base(){}

	virtual void visit(parse_node_visitor *)=0;
	virtual int get_node_id() const=0;
};
DECLARE_INTRUSIVE_NAMED_PTR(parse_node_base,parse_node);

template<class T,class U> boost::intrusive_ptr<T> dynamic_node_cast(
	const boost::intrusive_ptr<U> &_other)
{
	if (_other->get_node_id()==T::node_id)
		return static_cast<T*>(_other.get());
	return boost::intrusive_ptr<T>();
}

template<class T,class U> boost::intrusive_ptr<T> static_node_cast(
	const boost::intrusive_ptr<U> &_other)
{		
	return static_cast<T*>(_other.get());
}

///////////////////////////////////////////////////////////////////////////////////////
////// Syntax node
///////////////////////////////////////////////////////////////////////////////////////

//Append list results of two statements
struct JAMLIB_EXPORT append_node : public parse_node_base
{
	enum{node_id=0};
	parse_node_ptr left_tree_,right_tree_;

	//left_tree  - more append_node by left-recursion
	//right_tree - single rule
	append_node(parse_node_ptr left_tree,parse_node_ptr right_tree) :
		left_tree_(left_tree), right_tree_(right_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(append_node);

//Evaluate logical expression (with short-circuit support)
//NOTE: this node has some non-trivial constructor!
struct JAMLIB_EXPORT eval_node : public parse_node_base
{
	enum{node_id=1};
	parse_node_ptr left_tree_,right_tree_;
	condition_type operation_;

	//left_tree  - the left part of expression
	//right_tree - the right part of expression
	//operation  - the logical operation
	eval_node(parse_node_ptr left_tree,parse_node_ptr right_tree,condition_type operation);
	
	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(eval_node);

//"for x in y" construction
struct JAMLIB_EXPORT foreach_node : public parse_node_base
{
	enum{node_id=2};
	parse_node_ptr var_values_tree_, rule_tree_;
	jam_string index_var_name_;
	bool is_var_local_;

	//var_values_tree - tree evalutaing to the list of variable values
	//rule_tree       - the body of foreach statement
	//index_var_name  - name of the index variable
	//is_var_local    - flag indicating the the variable is local
	foreach_node(parse_node_ptr var_values_tree,parse_node_ptr rule_tree,
		const jam_string& index_var_name, bool is_var_local) :
		var_values_tree_(var_values_tree),rule_tree_(rule_tree),
		index_var_name_(index_var_name), is_var_local_(is_var_local) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(foreach_node);

//if-then-else construction
struct JAMLIB_EXPORT if_node : public parse_node_base
{
	enum{node_id=3};
	parse_node_ptr condition_tree_,then_tree_, else_tree_;

	//condition_tree - tree evaluating to condition
	//then_tree      - tree evaluating to 'then' body
	//else_tree      - tree evaluating to 'else' body
	if_node(parse_node_ptr condition_tree,
		parse_node_ptr then_tree, parse_node_ptr else_tree) : 
		condition_tree_(condition_tree), then_tree_(then_tree),
		else_tree_(else_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(if_node);

//'include' statement
struct JAMLIB_EXPORT include_node : public parse_node_base
{
	enum{node_id=4};
	parse_node_ptr file_name_tree_;

	//file_name_tree - list of files to include (can only do 1)
	include_node(parse_node_ptr file_name_tree) : 
		file_name_tree_(file_name_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(include_node);

//list compilation and expansion
struct JAMLIB_EXPORT list_node : public parse_node_base
{
	enum{node_id=5};
	jam_string expand_string_;

	//expand_string - string to expand
	list_node(const jam_string &expand_string) : 
		expand_string_(expand_string) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(list_node);

//declare and set local variables
struct JAMLIB_EXPORT local_node : public parse_node_base
{
	enum{node_id=6};
	parse_node_ptr var_list_tree_, val_list_tree_, rule_list_tree_;

	//var_list_tree  - list of variables
	//val_list_tree  - list of variable values
	//rule_list_tree - rules body for the local variables block.
	local_node(parse_node_ptr var_list_tree, parse_node_ptr val_list_tree,
		parse_node_ptr rule_list_tree) : var_list_tree_(var_list_tree),
		val_list_tree_(val_list_tree), rule_list_tree_(rule_list_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(local_node);

//module declaration
struct JAMLIB_EXPORT module_node : public parse_node_base
{
	enum{node_id=7};
	parse_node_ptr module_name_tree_, module_content_tree_;

	//module_name_tree    - tree evaluating to module name
	//module_content_tree - tree with module code
	module_node(parse_node_ptr module_name_tree, parse_node_ptr 
		module_content_tree) : module_name_tree_(module_name_tree), 
		module_content_tree_(module_content_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(module_node);

//TODO: look this in detail!
struct JAMLIB_EXPORT class_node : public parse_node_base
{
	enum{node_id=8};
	parse_node_ptr class_name_tree_, right_;

	class_node(parse_node_ptr class_name_tree,
		parse_node_ptr right) : class_name_tree_(class_name_tree),
		right_(right) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(class_node);

//The most complex node. I don't even know what it does! :)
struct JAMLIB_EXPORT null_node : public parse_node_base
{
	enum{node_id=9};
	null_node() {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(null_node);

//"run rule under influence of on-target variables" - whatever it means
struct JAMLIB_EXPORT on_node : public parse_node_base
{
	enum{node_id=10};
	parse_node_ptr list_tree_, rule_tree_;

	//list_tree - list of variables to use on target
	//rule_tree - rule to run
	on_node(parse_node_ptr list_tree,parse_node_ptr rule_tree) :
		list_tree_(list_tree), rule_tree_(rule_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(on_node);

//Single user-defined rule
struct JAMLIB_EXPORT rule_node : public parse_node_base
{
	enum{node_id=11};
	parse_node_ptr params_tree_;
	jam_string name_;

	//params_tree - parameters (list of lists) of rule, recursing left
	//name        - name of user defined rule
	rule_node(parse_node_ptr params_tree, const jam_string &name) :
		params_tree_(params_tree), name_(name) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(rule_node);

//A chain of rules
struct JAMLIB_EXPORT rules_node : public parse_node_base
{
	enum{node_id=12};
	parse_node_ptr rule_tree_, chained_rules_tree_;

	//rule_tree	         - a single rule body
	//chained_rules_tree - more rules_node by right-recursion
	rules_node(parse_node_ptr rule_tree, parse_node_ptr chained_rules_tree) :
		rule_tree_(rule_tree), chained_rules_tree_(chained_rules_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(rules_node);

//"set variable" statement
struct JAMLIB_EXPORT set_node : public parse_node_base
{
	enum{node_id=13};
	parse_node_ptr var_names_tree_, val_tree_;
	assignment_operation operation_;

	//var_names_tree - variable names
	//val_tree       - variable values 
	//operation      - assignment operation ASSIGN_SET/APPEND/DEFAULT
	set_node(parse_node_ptr var_names_tree,parse_node_ptr val_tree,
		assignment_operation operation) : var_names_tree_(var_names_tree),
		val_tree_(val_tree), operation_(operation) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(set_node);

//compile the "on =" (set variable on exec) statement
struct JAMLIB_EXPORT settings_node : public parse_node_base
{
	enum{node_id=14};
	parse_node_ptr names_tree_, target_names_tree_, val_tree_;
	assignment_operation operation_;

	//names_tree        - variable names
	//target_names_tree - tree evaluating to target names
	//val_tree          - tree evaluating to the variable value 
	//operation         - assignment ASSIGN_SET/APPEND (DEFAULT has no effect)
	settings_node(parse_node_ptr names_tree,parse_node_ptr target_names_tree,
		parse_node_ptr val_tree, assignment_operation operation) :
		names_tree_(names_tree), target_names_tree_(target_names_tree),
		val_tree_(val_tree), operation_(operation) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(settings_node);

//support for `rule` - save parse tree (i.e. compile rule definition)
struct JAMLIB_EXPORT setcomp_node : public parse_node_base
{
	enum{node_id=15};
	parse_node_ptr rule_content_tree_, param_list_tree_;
	jam_string rule_name_;
	bool is_local_;

	//rule_content_tree - tree evaluating to the body of rule definition
	//param_list_tree   - tree evaluating to the list of parameters
	//rule_name         - name of the rule
	//is_local          - flag indicating that rule is local to the current module
	setcomp_node(parse_node_ptr rule_content_tree,parse_node_ptr param_list_tree,
		const jam_string &rule_name, bool is_local) : 
		rule_content_tree_(rule_content_tree), param_list_tree_(param_list_tree),
		rule_name_(rule_name), is_local_(is_local) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(setcomp_node);

//support for `actions` - save execution string (for evaluation)
struct JAMLIB_EXPORT setexec_node : public parse_node_base
{
	enum{node_id=16};
	parse_node_ptr bind_var_tree_;
	jam_string action_name_,os_string_;
	int flags_;

	//bind_var_tree - tree evaluating to the list of `bind` variables
	//action_name   - action name
	//os_string_    - command string
	//flags         - execution flags
	setexec_node(parse_node_ptr bind_var_tree,const jam_string &action_name,
		const jam_string &os_string, int flags) : 
		bind_var_tree_(bind_var_tree), action_name_(action_name), 
		os_string_(os_string), flags_(flags) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(setexec_node);

//switch rule
struct JAMLIB_EXPORT switch_node : public parse_node_base
{
	enum{node_id=17};
	parse_node_ptr value_tree_, case_tree_;

	//value_tree  - tree evaluating to switch value (only 1st used)
	//case_tree   - tree evaluating to 'switch' body:
	//
	//	chain_node->left_tree  - 1st case (string_node)
	//	chain_node->right_tree - next cases (more 'chain_node's)
	//	
	//	  string_node->left_tree - parse tree to execute
	//	  string_node->string    - argument to match
	switch_node(parse_node_ptr value_tree,parse_node_ptr case_tree) :
		value_tree_(value_tree), case_tree_(case_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(switch_node);

//'chain' node for recursive structures. 
//NOTE: added by AB, not present in original JAM!
struct JAMLIB_EXPORT chain_node : public parse_node_base
{
	enum{node_id=18};
	parse_node_ptr left_tree_, right_tree_;

	//left_tree  - left tree
	//chain_tree - right tree
	chain_node(parse_node_ptr left_tree,parse_node_ptr right_tree) :
		left_tree_(left_tree), right_tree_(right_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(chain_node);

//The leaf of 'cases' tree
//NOTE: added by AB, not present in original JAM!
struct JAMLIB_EXPORT case_node : public parse_node_base
{
	enum{node_id=19};
	parse_node_ptr body_tree_;
	jam_string value_;

	//body_tree  - case body
	//chain_tree - value to match
	case_node(parse_node_ptr body_tree,const jam_string &value) :
		body_tree_(body_tree), value_(value) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(case_node);

//'lol' node for list-of-lists. 
//NOTE: added by AB, not present in original JAM!
struct JAMLIB_EXPORT lol_node : public parse_node_base
{
	enum{node_id=20};
	parse_node_ptr list_elem_,chained_list_;

	//list_elem    - element of the list
	//chained_list - chained lol nodes
	lol_node(parse_node_ptr list_elem,parse_node_ptr chained_list) :
		list_elem_(list_elem),chained_list_(chained_list) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(lol_node);

//'while' loop rule
struct JAMLIB_EXPORT while_node : public parse_node_base
{
	enum{node_id=21};
	parse_node_ptr condition_tree_, body_tree_;

	//condition_tree - tree evaluating to loop condition
	//body_tree      - loop body
	while_node(parse_node_ptr condition_tree,parse_node_ptr body_tree) :
		condition_tree_(condition_tree), body_tree_(body_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(while_node);

//'return' statement of a rule
struct JAMLIB_EXPORT return_node : public parse_node_base
{
	enum{node_id=22};
	parse_node_ptr list_res_;

	//list_res - tree evaluating to rule result
	return_node(parse_node_ptr list_res) : list_res_(list_res) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(return_node);

//Node containing the opening '{', the block body and the closing '}'
struct JAMLIB_EXPORT left_brace_node : public parse_node_base
{
	enum{node_id=23};
	parse_node_ptr body_tree_,rbracket_tree_;

	//body_tree     - tree containing the body of this block
	//rbracket_tree - tree containing the right bracket
	left_brace_node(parse_node_ptr body_tree,parse_node_ptr rbracket_tree) :
		body_tree_(body_tree),rbracket_tree_(rbracket_tree) {}

	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(left_brace_node);

//The closing right brace
struct JAMLIB_EXPORT right_brace_node : public parse_node_base
{
	enum{node_id=24};
	right_brace_node() {}
	virtual void visit(parse_node_visitor *);
	virtual int get_node_id() const {return node_id;}
};
DECLARE_INTRUSIVE_PTR(right_brace_node);

///////////////////////////////////////////////////////////////////////////////////////
////// Visitor (fully inline, no need for JAMLIB_EXPORT)
///////////////////////////////////////////////////////////////////////////////////////
struct parse_node_visitor
{
	virtual ~parse_node_visitor(){}

	virtual void visit_append_node(parse_node_base*,parse_node_ptr left_tree,
		parse_node_ptr right_tree){}

	virtual void visit_eval_node(parse_node_base*,parse_node_ptr left_tree,
		parse_node_ptr right_tree, condition_type operation){}

	virtual void visit_foreach_node(parse_node_base*,parse_node_ptr var_values_tree,
		parse_node_ptr rule_tree, const jam_string& index_var_name, 
		bool is_var_local){}

	virtual void visit_if_node(parse_node_base*,parse_node_ptr condition_tree,
		parse_node_ptr then_tree, parse_node_ptr else_tree){}

	virtual void visit_include_node(parse_node_base*,parse_node_ptr file_name_tree){}

	virtual void visit_list_node(parse_node_base*,const jam_string &expand_string){}

	virtual void visit_local_node(parse_node_base*,parse_node_ptr var_list_tree, 
		parse_node_ptr val_list_tree, parse_node_ptr rule_list_tree){}

	virtual void visit_module_node(parse_node_base*,parse_node_ptr module_name_tree, 
		parse_node_ptr module_content_tree){}

	virtual void visit_class_node(parse_node_base*,parse_node_ptr class_name_tree,
		parse_node_ptr right){}

	virtual void visit_null_node(parse_node_base*){}

	virtual void visit_on_node(parse_node_base*,parse_node_ptr list_tree,
		parse_node_ptr rule_tree){}

	virtual void visit_rule_node(parse_node_base*,parse_node_ptr params_tree, 
		const jam_string &name){}

	virtual void visit_rules_node(parse_node_base*,parse_node_ptr rule_tree, 
		parse_node_ptr chained_rules_tree){}

	virtual void visit_set_node(parse_node_base*,parse_node_ptr var_names_tree,
		parse_node_ptr val_tree, assignment_operation operation){}

	virtual void visit_settings_node(parse_node_base*,parse_node_ptr names_tree,
		parse_node_ptr target_names_tree,parse_node_ptr val_tree, 
		assignment_operation operation){}

	virtual void visit_setcomp_node(parse_node_base*,parse_node_ptr rule_content_tree,
		parse_node_ptr param_list_tree, const jam_string &rule_name, 
		bool is_local){}

	virtual void visit_setexec_node(parse_node_base*,parse_node_ptr bind_var_tree,
		const jam_string &action_name, const jam_string &os_string, 
		int flags){}

	virtual void visit_switch_node(parse_node_base*,parse_node_ptr value_tree,
		parse_node_ptr case_tree){}

	virtual void visit_chain_node(parse_node_base*,parse_node_ptr left_tree,
		parse_node_ptr right_tree){}

	virtual void visit_case_node(parse_node_base*,parse_node_ptr body_tree,
		const jam_string &value){}

	virtual void visit_while_node(parse_node_base*,parse_node_ptr condition_tree,
		parse_node_ptr body_tree){}

	virtual void visit_lol_node(parse_node_base*,parse_node_ptr list_elem,
		parse_node_ptr chained_list){}

	virtual void visit_return_node(parse_node_base*, parse_node_ptr list_res){};

	virtual void visit_left_brace_node(parse_node_base*, parse_node_ptr body_tree,
		parse_node_ptr rbracket_tree) {}

	virtual void visit_right_brace_node(parse_node_base*) {}
};

#endif
